Skip to main content

Hook Modules

Overview

Hook modules (IHook) enable custom logic injection at specific points in the transaction lifecycle, allowing for pre- and post-execution checks and actions.

Interface Definition

interface IHook is IModule {
function preCheck(
address msgSender,
uint256 msgValue,
bytes calldata msgData
) external returns (bytes memory hookData);

function postCheck(bytes calldata hookData) external;
}

Implementation Example: Value Limiting Hook

Based on the provided MockHook implementation:

contract ValueLimitHook is IHook {
// State variables
mapping(address => uint256) public userMaxEthAmount;
mapping(address => bool) public isUserInitialized;

// Core Module Functions
function onInstall(bytes calldata data) external override {
uint256 maxAmount = abi.decode(data, (uint256));
userMaxEthAmount[msg.sender] = maxAmount;
isUserInitialized[msg.sender] = true;
}

function onUninstall(bytes calldata) external override {
isUserInitialized[msg.sender] = false;
delete userMaxEthAmount[msg.sender];
}

function isModuleType(uint256 moduleTypeId) external pure override returns (bool) {
return moduleTypeId == MODULE_TYPE_HOOK;
}

function isInitialized(address smartAccount) external view override returns (bool) {
return isUserInitialized[smartAccount];
}

// Hook-specific Functions
function preCheck(
address msgSender,
uint256 msgValue,
bytes calldata msgData
) external override returns (bytes memory hookData) {
require(
msgValue <= userMaxEthAmount[msgSender],
"Transfer value exceeds max amount"
);
return abi.encode(msgSender, msgValue, msgData);
}

function postCheck(bytes calldata hookData) external override {
// Post-execution validation if needed
(address sender, uint256 value, bytes memory data) = abi.decode(
hookData,
(address, uint256, bytes)
);
}
}

Extended Implementation: Activity Monitor Hook

contract ActivityMonitorHook is IHook {
// Track transaction counts and timing
mapping(address => uint256) public lastActivityTimestamp;
mapping(address => uint256) public dailyTransactionCount;

uint256 public constant MAX_DAILY_TRANSACTIONS = 10;
uint256 public constant DAY_SECONDS = 86400;

event ActivityRegistered(address indexed account, uint256 timestamp);

function preCheck(
address msgSender,
uint256 msgValue,
bytes calldata msgData
) external returns (bytes memory hookData) {
uint256 currentDay = block.timestamp / DAY_SECONDS;
uint256 lastDay = lastActivityTimestamp[msgSender] / DAY_SECONDS;

if (currentDay > lastDay) {
dailyTransactionCount[msgSender] = 0;
}

require(
dailyTransactionCount[msgSender] < MAX_DAILY_TRANSACTIONS,
"Daily transaction limit exceeded"
);

hookData = abi.encode(msgSender, block.timestamp);
return hookData;
}

function postCheck(bytes calldata hookData) external {
(address account, uint256 timestamp) = abi.decode(hookData, (address, uint256));
lastActivityTimestamp[account] = timestamp;
dailyTransactionCount[account]++;
emit ActivityRegistered(account, timestamp);
}
}

Common Use Cases

  1. Transaction Monitoring

    • Rate limiting
    • Value limits
    • Activity tracking
  2. Security Checks

    • Address validation
    • Permission verification
    • Risk assessment
  3. State Management

    • Balance tracking
    • Counter management
    • Status updates

Security Considerations

  1. Pre-execution Safety

    • Validate parameters
    • Check conditions
    • Handle edge cases
  2. Post-execution Verification

    • Verify state changes
    • Update tracking
    • Emit events
  3. Data Handling

    • Safe encoding/decoding
    • Parameter validation
    • State consistency

Best Practices

  1. Hook Design

    • Clear conditions
    • Efficient checks
    • Proper events
  2. State Management

    • Atomic updates
    • Consistent state
    • Safe operations
  3. Error Handling

    • Clear messages
    • Proper reverts
    • Parameter validation